import datetime
import os

import pymongo
from django.shortcuts import render
from langchain.chains.llm import LLMChain
from langchain_community.llms.tongyi import Tongyi
from langchain_core.prompts import PromptTemplate
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from django.http import JsonResponse

from attractions.models import UserFavorite, ScenicSpot
from attractions.seratt import ScenicSpotSerializer
from tools.mongo import mongo1
from tools.myredis import r
from attractions import models, seratt
import json

from xm3 import settings


class ScenicRecommendationView(APIView):
    def get(self, request):
        # 获取用户 ID
        user_id = request.query_params.get('userid')
        if not user_id:
            return JsonResponse({'error': '缺少用户 ID'}, status=400)

        # 尝试查询用户
        try:
            user = models.Users.objects.get(id=user_id)
        except models.Users.DoesNotExist:
            return JsonResponse({'error': '用户不存在'}, status=404)

        # 获取用户访问过的景点
        visited_scenics = models.UserScenic.objects.filter(user_id=user.id).values_list('attraction_id', flat=True)
        print(f"用户访问过的景点：{list(visited_scenics)}")  # 打印用户访问过的景点

        # 如果用户没有访问过任何景点，返回空结果
        if not visited_scenics:
            return JsonResponse({'recommendation': []})

        # 获取浏览过相同景点的其他用户
        related_users = models.UserScenic.objects.filter(
            attraction_id__in=visited_scenics,
            user_id__isnull=False
        ).exclude(user_id=user.id).values_list('user_id', flat=True)
        print(f"相关用户：{list(related_users)}")  # 打印相关用户

        # 限制为前 5 个用户
        related_users = list(related_users.distinct())[:5]

        # 如果没有找到相关用户，返回空结果
        if not related_users:
            print("没有找到相关用户，返回空结果")  # 打印调试信息
            return JsonResponse({'recommendation': []})

        # 获取这些用户浏览过的景点
        other_scenics = models.UserScenic.objects.filter(user_id__in=related_users).values_list('attraction_id',
                                                                                                flat=True)
        print(f"其他用户浏览过的景点：{list(other_scenics)}")  # 打印其他用户浏览过的景点

        # 将景点 ID 存入 Redis 并计算差集
        user_key = f"user:{user.id}:visited"
        related_key = f"related:scenics"
        diff_key = f"recommendation:user:{user.id}"

        # 清除之前的值
        r.str_del(user_key)
        r.str_del(related_key)
        r.str_del(diff_key)

        # 存储当前用户访问过的景点（使用集合）
        for scenic_id in visited_scenics:
            r.sadd(user_key, str(scenic_id))

        # 存储相关用户访问过的景点（使用集合）
        for scenic_id in other_scenics:
            r.sadd(related_key, str(scenic_id))

        # 计算差集并存储到新的键中
        r.r.sdiffstore(diff_key, related_key, user_key)

        # 获取差集中的景点 ID
        diff_attraction_ids = r.sgetall(diff_key)
        diff_attraction_ids = [int(attraction_id.decode('utf-8')) for attraction_id in diff_attraction_ids]

        # 查询推荐的景点
        recommendation_scenics = models.Attractions.objects.filter(id__in=diff_attraction_ids)

        # 序列化推荐的景点
        scenic_list = []
        for scenic in recommendation_scenics:
            scenic_data = {
                'id': scenic.id,
                'name': scenic.name,
                'city': scenic.city.name,
                'desc': scenic.desc,
                'price': scenic.price,
                'img': scenic.img.url if scenic.img else None
            }
            scenic_list.append(scenic_data)

        return JsonResponse({'recommendation': scenic_list}, safe=False)


class Citylist(APIView):
    def get(self, request):
        # 尝试从缓存获取数据
        cached_city = r.get_str('citylist')
        if cached_city:
            return Response({'code': 200,
                             'message': 'success',
                             'data': json.loads(cached_city)})

        try:
            # 数据库查询与序列化
            city = models.City.objects.all()
            serializer = seratt.CitySerializer(city, many=True)
            city_data = serializer.data

            r.set_str('citylist', json.dumps(city_data))

            # 返回响应
            return Response({
                'code': 200,
                'message': 'success',
                'data': city_data
            })
        except Exception as e:
            return Response({
                'code': 500,
                'message': f'服务器错误: {str(e)}'
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class Attractionslist(APIView):
    def get(self, request):
        attlist = r.get_str('attlist')
        if attlist:
            return Response({'code': 200, 'message': 'success', 'data': json.loads(attlist)})
        try:
            attractions = models.Attractions.objects.all()
            sattraction = seratt.AttractionsSerializer(attractions, many=True)
            r.set_str('attlist', json.dumps(sattraction.data))
            return Response({'code': 200, 'message': 'success', 'data': sattraction.data})
        except Exception as e:
            return Response({'code': 500, 'message': f'服务器错误: {str(e)}'},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class Attractionsinfo(APIView):
    def get(self, request):
        attractionid = request.query_params.get('attractionid')
        if not attractionid:
            return Response({'code': 400, 'message': '参数缺失'}, status=400)
        cached_data = r.get_str(f'attraction_{attractionid}')
        if cached_data:
            return Response({'code': 200, 'message': 'success', 'data': json.loads(cached_data)})
        try:
            attraction = models.Attractions.objects.get(id=attractionid)
            sattraction = seratt.AttractionsSerializer(attraction)  # 单对象序列化
            r.set_str(f'attraction_{attractionid}', json.dumps(sattraction.data))
            return Response({'code': 200, 'message': 'success', 'data': sattraction.data})
        except models.Attractions.DoesNotExist:
            return Response({'code': 404, 'message': '景点不存在'}, status=404)
        except ValueError:
            return Response({'code': 400, 'message': '参数错误'}, status=400)
        except Exception as e:
            return Response({'code': 500, 'message': f'服务器错误: {str(e)}'}, status=500)
# class Ticketupdete(APIView):
#     '''门票重置'''
#     def post(self, request):
#         from django.utils import timezone
#         from datetime import timedelta
#
#         try:
#             # 获取所有门票
#             tickets = models.AttractionTicke.objects.all()
#
#             # 更新每个门票的库存和有效期
#             for ticket in tickets:
#                 ticket.stock = 100  # 重置库存为100
#                 ticket.valid_date = timezone.now().date() + timedelta(days=1)  # 设置为次日
#                 ticket.save()
#
#             # 清除redis中的门票缓存
#             r.delete_pattern('ticketlist*')
#
#             return Response({'code': 200, 'message': '门票重置成功'})
#         except Exception as e:
#             return Response({'code': 500, 'message': f'重置失败: {str(e)}'}, status=500)

class Ticketlist(APIView):
    '''门票列表'''
    # 获取请求参数attid
    def get(self, request):
        attid= request.query_params.get('attid')
        # 从redis中获取ticketlist
        ticketlist = r.get_str('ticketlist'+str(attid))
        # 如果redis中有ticketlist，则返回
        # if ticketlist:
        #     return Response({'code': 200, 'message': 'success', 'data': json.loads(ticketlist)})
        # 如果redis中没有ticketlist，则从数据库中获取
        try:
            ticket = models.AttractionTicke.objects.filter(attraction_id=attid)
            # 序列化ticket                                                                                                                                                                          
            sticket = seratt.AttractionTickeSerializer(ticket, many=True)
            # 将序列化后的ticket存入redis
            r.setex_str('ticketlist'+str(attid), 60*60*24 , json.dumps(sticket.data))
            # 返回序列化后的ticket
            return Response({'code': 200, 'message': 'success', 'data': sticket.data})
        # 如果出现异常，则返回错误信息
        except Exception as e:
            return Response({'code': 500, 'message': f'服务器错误: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

class Commentlist(APIView):
    '''评论列表'''
    def get(self, request):
        # 获取请求参数
        attid = request.query_params.get('attid')
        # 从redis中获取commentlist
        commentlist = r.get_str('commentlist'+str(attid))
        # 如果redis中有commentlist，则返回
        # if commentlist:
        #     return Response({'code': 200, 'message': 'success', 'data': json.loads(commentlist)})
        # 如果redis中没有commentlist，则从数据库中获取
        try:
            comment = models.UserComment.objects.filter(attraction_id=attid)
            # 序列化Usercomment
            scomment = seratt.CommentSerializer(comment, many=True)
            # 将序列化后的Usercomment存入redis
            r.setex_str('commentlist'+str(attid), 60*60*24,json.dumps(scomment.data) )
            # 返回序列化后的Usercomment
            return Response({'code': 200, 'message': 'success', 'data': scomment.data})
        # 如果出现异常，则返回错误信息
        except Exception as e:
            return Response({'code': 500, 'message': f'服务器错误: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)




from urllib import parse


class CommentView(APIView):
    '''评论灌水'''

    def post(self, request):
        # 获取参数
        user_id = request.data.get('user_id')
        product_id = request.data.get('product_id')
        comment_text = request.data.get('comment_text')
        # file = request.FILES.get('comment_image')
        pid = request.data.get('pid')

        if not all([user_id, product_id, comment_text]):
            return JsonResponse({"error": "Missing required parameters."}, status=400)

        flag = Tongyi.check_words(comment_text)
        if flag == False:
            return Response({"code": 10010})
        # 图片上传到 static/upload 目录
        # if file:
        #     file_path = os.path.join(settings.MEDIA_ROOT, file.name)
        #     with open(file_path, 'wb+') as destination:
        #         for chunk in file.chunks():
        #             destination.write(chunk)

        # 使用 Langchain 的 Tongyi 模块判断好评、中评还是差评
        llm = Tongyi()  # 假设 Tongyi 模块已经正确导入和初始化
        prompt_template = PromptTemplate(
            input_variables=["comment_text"],
            template="你是一名专业的评价分析员，你的任务是根据用户的评价判断其属于好评、中评还是差评。以下是用户的评价：{comment_text}。在判断时，请遵循以下标准和限制条件：好评意味着用户对产品或服务非常满意，表达了高度的赞扬，可能会提到产品的优点，如出色的性能、优质的服务、高性价比、外观精美、超出预期的体验等；中评表示用户对产品或服务有一定的满意度，但也可能存在一些小的不满或建议，用户可能会认为部分功能未达到预期，但整体上还是可以接受；差评表示用户对产品或服务非常不满意，可能会提及质量问题、服务态度不好、严重的性能缺陷、发货延迟、功能与描述不符等问题。请仅回答好评、中评或差评，不允许出现其他无关信息。"
        )
        chain = LLMChain(llm=llm, prompt=prompt_template)
        rating_result = chain.run({"comment_text": comment_text})

        # 写入 MongoDB 评价表

        db = mongo1['s1']  # 替换为实际的数据库名称
        collection = db['comments']
        comment_data = {
            'user_id': user_id,
            'product_id': product_id,
            'comment_text': comment_text,
            # 'comment_image': 'static/upload/' + str(file.name),
            'rating': rating_result,
            'created_at': datetime.datetime.now(),
            'pid': pid
        }
        collection.insert_one(comment_data)
        # 更新商品表中总评价数、更新好评中评

        return JsonResponse({"message": "Comment added successfully."}, status=201)

    def get(self, request):
        try:
            # 调用 getmongo 函数获取 MongoDB 客户端
            client= mongo1
            if client is None:
                return Response({
                    'code': 500,
                    'message': '无法连接到 MongoDB 服务器'
                })

            # 选择数据库
            db = client['s1']
            print(db)
            # 选择集合
            collection= db['comments']
            print(collection)

            # 指定投影，只返回 user、comment 和 is_good_review 字段，排除 _id 字段
            projection= {"user": 1, "comment": 1, "is_good_review": 1, "_id": 0}
            print(projection)


            # 查询所有文档并应用投影
            data= collection.find({}, projection)
            print(data)

            # 将查询结果转换为列表
            result= list(data)
            print(result)

            # # 关闭 MongoDB 连接
            # client.close()

            return Response({
                'code': 200,
                'message': 'success',
                'data':result
            })
        except Exception as e:
            return Response({
                'code': 500,
                'message': f'获取数据时出现错误: {str(e)}'
            })




from django.db.models import Q

class SpotRecommendView(APIView):
    '''推荐攻略'''
    def get(self, request):
        user = request.user
        # 协同过滤算法实现‌:ml-citation{ref="7" data="citationList"}
        # 获取用户喜欢的景点
        favorite_spots = UserFavorite.objects.filter(user=user).values_list('spot', flat=True)
        # 获取与用户喜好相似的用户的喜好
        similar_users = UserFavorite.objects.filter(
            spot__in=favorite_spots
        ).exclude(user=user).values('user')
        
        # 组合推荐逻辑
        # 根据用户喜好和相似用户的喜好，推荐景点
        recommend_spots = ScenicSpot.objects.filter(
            Q(tags__contains=user.preference_tags) |  # 内容推荐
            Q(id__in=UserFavorite.objects.filter(user__in=similar_users))  # 协同过滤
        ).distinct()[:10]
        
        # 将推荐景点序列化
        serializer = ScenicSpotSerializer(recommend_spots, many=True)
        return Response(serializer.data)
